package fr.asip.cps3.exemple.modele.traitements.fonctionsPKCS11.objets;

import iaik.pkcs.pkcs11.wrapper.CK_ATTRIBUTE;
import iaik.pkcs.pkcs11.wrapper.PKCS11;
import iaik.pkcs.pkcs11.wrapper.PKCS11Constants;
import iaik.pkcs.pkcs11.wrapper.PKCS11Exception;

import java.util.LinkedHashMap;

import org.apache.log4j.Logger;

import fr.asip.cps3.exemple.modele.exceptions.ExceptionObjet;
import fr.asip.cps3.exemple.modele.exceptions.ExceptionProgrammeExemple;
import fr.asip.cps3.exemple.modele.exceptions.ExceptionTraitement;
import fr.asip.cps3.exemple.modele.resultats.Resultat;
import fr.asip.cps3.exemple.modele.resultats.ResultatTripleInfo;
import fr.asip.cps3.exemple.modele.traitements.util.TraitementsUtil;

/**
 * Classe de traitement : Recherche d'objets
 *
 */
public class RechercheObjets{

	/**
	 * Le loggeur
	 */
	private static Logger log = Logger.getLogger(RechercheObjets.class);
	
	/**
	 * Renvoie le résultat de l'opération
	 * @param librairie Librairie PKCS#11 à utiliser
	 * @param idSession Identifiant de la session
	 * @param typeObjet Type d'objets recherchés
	 * @param filtre Filtre de recherche (optionnel)
	 * @return Résultat de l'opération
	 * @throws ExceptionTraitement
	 * @throws ExceptionObjet
	 */
	public static Resultat traitementP11(PKCS11 librairie, long idSession, int typeObjet, String filtre) throws ExceptionObjet, ExceptionTraitement {
		
		try {
			
			if(librairie != null) {

				String type = "inconnu";
				CK_ATTRIBUTE[] attributes = null;
				
				// Construction des critères de recherche
				switch(typeObjet) {
				
					case TraitementsUtil.TYPE_OBJET_CERTIFICAT:
						type = "certificat";
						attributes = new CK_ATTRIBUTE[1];
						attributes[0] = new CK_ATTRIBUTE();
						attributes[0].type = PKCS11Constants.CKA_CLASS;				
						attributes[0].pValue = PKCS11Constants.CKO_CERTIFICATE;
						break;
						
					case TraitementsUtil.TYPE_OBJET_CLE_PUBLIQUE:
						type = "clé publique";
						attributes = new CK_ATTRIBUTE[1];
						attributes[0] = new CK_ATTRIBUTE();				
						attributes[0].type = PKCS11Constants.CKA_CLASS;				
						attributes[0].pValue = PKCS11Constants.CKO_PUBLIC_KEY;
						break;
						
					case TraitementsUtil.TYPE_OBJET_CLE_PRIVE:
						type = "clé privée";
						attributes = new CK_ATTRIBUTE[1];
						attributes[0] = new CK_ATTRIBUTE();				
						attributes[0].type = PKCS11Constants.CKA_CLASS;				
						attributes[0].pValue = PKCS11Constants.CKO_PRIVATE_KEY;
						break;
						
					case TraitementsUtil.TYPE_OBJET_DONNEES_APPLICATIVES:
						type = "de données applicatives";
						int i = 1;
						if(filtre != null)
							i = 2;
						attributes = new CK_ATTRIBUTE[i];
						
						attributes[0] = new CK_ATTRIBUTE();				
						attributes[0].type = PKCS11Constants.CKA_CLASS;				
						attributes[0].pValue = PKCS11Constants.CKO_DATA;
						
						if(i > 1 && filtre != null) {

							attributes[1] = new CK_ATTRIBUTE();				
							attributes[1].type = PKCS11Constants.CKA_LABEL;				
							attributes[1].pValue = filtre;
						
						}
							
						break;
					
					default:
						// Si le type d'objet demandé est non géré on lève une exception
						log.error("Type d'objet "+typeObjet+" non géré");
						throw new ExceptionTraitement(ExceptionProgrammeExemple.TYPE_TRAITEMENT_OBJETS_RECHERCHE, "Type d'objet non géré");
				
				}
				
				ResultatTripleInfo resultat = new ResultatTripleInfo("Recherche d'objet(s) de type "+type+" (Initialisation (C_FindObjectsInit))","Recherche d'objet(s) de type "+type+" (Recherche (C_FindObjects))","Recherche d'objet(s) de type "+type+" (Finalisation (C_FindObjectsFinal))");
				LinkedHashMap<String,String> infos = new LinkedHashMap<String,String>();
					
				long[] objets = null;
				boolean erreurSurvenue = false;
				
				// Traitements à tester
				try {
					
					//# INIT #//
					librairie.C_FindObjectsInit(idSession, attributes);
					
				} catch (PKCS11Exception e) {
					
					log.info("Operation echouee : "+TraitementsUtil.retranscritCodeRetour(e.getErrorCode()));
					resultat.setCodeRetour(e.getErrorCode());
					
					erreurSurvenue = true;
					resultat.setCodeRetour2(TraitementsUtil.AUCUN_CODE_RETOUR);
					resultat.appendOperation2(" NON EXECUTEE!");
					resultat.setCodeRetour3(TraitementsUtil.AUCUN_CODE_RETOUR);
					resultat.appendOperation3(" NON EXECUTEE!");
				}
				
				try {
					
					//# RECHERCHE #//
					if(!erreurSurvenue)
						objets = librairie.C_FindObjects(idSession,32L);
	
				} catch (PKCS11Exception e) {
					
					log.info("Operation echouee : "+TraitementsUtil.retranscritCodeRetour(e.getErrorCode()));
					resultat.setCodeRetour2(e.getErrorCode());
					
					erreurSurvenue = true;
					resultat.setCodeRetour3(TraitementsUtil.AUCUN_CODE_RETOUR);
				}
	
				try {
					
					//# FINAL #//				
					if(!erreurSurvenue)
						librairie.C_FindObjectsFinal(idSession);
					
				} catch (PKCS11Exception e) {
					
					log.info("Operation echouee : "+TraitementsUtil.retranscritCodeRetour(e.getErrorCode()));
					resultat.setCodeRetour3(e.getErrorCode());
					
				}
				
				
				// Formatage du résultat et collecte d'objet
				if(objets != null && !erreurSurvenue) {
					infos.put("Nombre d'objets:",Integer.toString(objets.length));
					for (int i = 0; i < objets.length; i++) {
						long idObjet = objets[i];
						infos.put("Identifiant de l'objet "+(i+1),Long.toString(idObjet));
						
						try {
							
							// On stocke les objets clé dans le resultat
							if(attributes != null && attributes[0] != null)
								if((Long)(attributes[0].pValue) == PKCS11Constants.CKO_PRIVATE_KEY
										|| (Long)(attributes[0].pValue) == PKCS11Constants.CKO_PUBLIC_KEY
											|| (Long)(attributes[0].pValue) == PKCS11Constants.CKO_SECRET_KEY) {
									long[] cle = new long[2];
									cle[0] = idObjet;
									cle[1] = (Long)(attributes[0].pValue);
									resultat.getObjets().add(cle);
								}
							// On stocke les objets clé dans le resultat
							if(attributes != null && attributes[0] != null)
								if((Long)(attributes[0].pValue) == PKCS11Constants.CKO_DATA)
									resultat.getObjets().add(idObjet);
							
						} catch (ClassCastException e) {
	
							// Si une erreur Traitement est rencontrée on lève l'exception
							throw new ExceptionTraitement(ExceptionProgrammeExemple.TYPE_TRAITEMENT_OBJETS_RECHERCHE, "Une erreur est survenue lors de la recherche des objets");
							
						}
	
					}
				}
				
				resultat.setInfos(infos);
				return resultat;
			
				
			} else {
				
				// Si la librairie est nulle il est impossible de réaliser l'opération
				log.error("La librairie est nulle");
				throw new ExceptionTraitement(ExceptionProgrammeExemple.TYPE_TRAITEMENT_OBJETS_RECHERCHE, "La librairie est nulle");
				
			}
		
		} catch (Throwable t) {
			
			// Si une erreur Objet a été rencontrée on lève l'exception
			if(t instanceof ExceptionObjet)
				throw (ExceptionObjet)t;

			// Si une erreur Traitement a été rencontrée on lève l'exception
			if(t instanceof ExceptionTraitement)
				throw (ExceptionTraitement)t;
			
			// Si une erreur inattendue est survenue la logue et on la lève
			log.error("Une erreur inattendue est survenue : "+ExceptionProgrammeExemple.getStacktrace(t));
			throw new ExceptionTraitement(ExceptionProgrammeExemple.TYPE_TRAITEMENT_OBJETS_RECHERCHE, "Une erreur inattendue est survenue");
			
		}
		
	}
}
